package cn.bonoon.kernel.support.services;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Query;

import cn.bonoon.kernel.bytecode.EntityReader;
import cn.bonoon.kernel.descriptors.EntityDescriptor;
import cn.bonoon.kernel.events.ReadEvent;
import cn.bonoon.kernel.support.entities.EntityTree;
import cn.bonoon.kernel.support.models.AbstractAjaxNode;
import cn.bonoon.kernel.support.models.AbstractNode;
import cn.bonoon.kernel.support.services.handler.DeleteTreeHandler;
import cn.bonoon.kernel.support.services.handler.EditTreeHandler;
import cn.bonoon.kernel.support.services.handler.EditTreeMovableHandler;
import cn.bonoon.kernel.support.services.handler.OperateTreeInHandler;
import cn.bonoon.kernel.support.services.handler.OperateTreeOutHandler;
import cn.bonoon.kernel.support.services.handler.OperateTreeUpHandler;

@SuppressWarnings("unchecked")
public abstract class AbstractTreeService<E extends EntityTree<E>> extends AbstractService<E>{
	private String __loadChildren, __loadRoot;
	
	@Override
	protected void internalInit(EntityDescriptor entityDescriptor, Class<E> entityClass) {
		super.internalInit(entityDescriptor, entityClass);
		__loadRoot = searcher.treeRoot();
		__loadChildren = searcher.treeChildren();
		/*
		 * 重新设置对树进行删除
		 * 这里可以改成通过其它指定，指定删除该树节点的时候，把该树节点下的所有子节点都移动到另一个地方
		 */
		deleteHandler = new DeleteTreeHandler<E>(deleteHandler, deleteHandler, entityClass);
	}
	
	@Override
	protected void initMovableHandler(EntityDescriptor entityDescriptor, Class<E> entityClass) {
		editHandler = new EditTreeMovableHandler<E>(entityClass);
		//上移、下移、移进、移出
		handlers[OPERAND_MOVE_UP] = new OperateTreeUpHandler<E>(entityClass);
		handlers[OPERAND_MOVE_IN] = new OperateTreeInHandler<E>(entityClass);
		handlers[OPERAND_MOVE_OUT] = new OperateTreeOutHandler<E>(entityClass);
	}
	
	@Override
	protected void initUnmovableHandler(EntityDescriptor entityDescriptor, Class<E> entityClass) {
		editHandler = new EditTreeHandler<E>(entityClass);
	}
	
	@Override
	public List<E> tryLoadChildren(Long rid) {
		if(null != rid && rid > 0){
			return __list(domainClass, __loadChildren, rid);
		}
		return __list(domainClass, __loadRoot);
	}
	
	@Override
	public List<E> tryLoadRoot() {
		return __list(domainClass, __loadRoot);
	}
	
	@Override
	public <O> List<O> tryLoadChildren(Long rid, ReadEvent readEvent, Class<O> resultClassType) throws Exception {
		if(null != rid && rid > 0){
			EntityReader reader = searcher.getEntityReader(resultClassType);
			String ql = __nodeSelect(reader, __loadChildren);
			Query qt = entityManager.createQuery(ql).setParameter(1, rid);
			
			if(AbstractAjaxNode.class.isAssignableFrom(resultClassType)){
				return __ajaxNodes(qt, reader, readEvent);
			}
			
			if(AbstractNode.class.isAssignableFrom(resultClassType)){
				return __nodes(qt, reader, readEvent, ql);
			}
			
			return __nodes(qt, reader, readEvent);
		}
		return __nodes(readEvent, resultClassType);
	}
	
	@Override
	public <O> List<O> tryLoadRoot(ReadEvent readEvent, Class<O> resultClassType) throws Exception {
		return __nodes(readEvent, resultClassType);
	}
	
	private String __nodeSelect(EntityReader reader, String where){
		return "select " + reader.properties() + where + __orderby;
	}
	
	private <O> List<O> __nodes(Query query, EntityReader reader, ReadEvent readEvent) throws Exception{
		List<O> rs = new ArrayList<>();
		for(Object it : query.getResultList()){
			rs.add((O)reader.read(readEvent, it));
		}
		return rs;
	}
	
	private List<AbstractNode> __nodes(String ql, Long pid, EntityReader reader, ReadEvent readEvent) throws Exception{
		List<AbstractNode> rs = new ArrayList<>();
		Query qt = entityManager.createQuery(ql).setParameter(1, pid);
		for(Object it : qt.getResultList()){
			Object r = reader.read(readEvent, it);
			AbstractNode an = (AbstractNode)r;
			Long parent = an.getId();
			if(null != parent && parent > 0){
				an.setChildren(__nodes(ql, parent, reader, readEvent));
			}
			rs.add(an);
		}
		return rs;
	}
	
	private <O> List<O> __nodes(Query query, EntityReader reader, ReadEvent readEvent, String ql) throws Exception{
		List<O> rs = new ArrayList<>();
		for(Object it : query.getResultList()){
			Object r = reader.read(readEvent, it);
			rs.add((O)r);
			AbstractNode an = (AbstractNode)r;
			Long parent = an.getId();
			if(null != parent && parent > 0){
				an.setChildren(__nodes(ql, parent, reader, readEvent));
			}
		}
		return rs;
	}
	
	private <O> List<O> __ajaxNodes(Query query, EntityReader reader, ReadEvent readEvent) throws Exception{
		List<O> rs = new ArrayList<>();
		for(Object it : query.getResultList()){
			Object r = reader.read(readEvent, it);
			rs.add((O)r);
			AbstractAjaxNode aan = (AbstractAjaxNode)r;
			int size = aan.getSize();
			aan.setState(size == 0 ? AbstractAjaxNode.OPEN : AbstractAjaxNode.CLOSED);
		}
		return rs;
	}
	
	private <O> List<O> __nodes(ReadEvent readEvent, Class<O> resultClassType)throws Exception{
		EntityReader reader = searcher.getEntityReader(resultClassType);
		Query query = entityManager.createQuery(__nodeSelect(reader, __loadRoot));
		if(AbstractAjaxNode.class.isAssignableFrom(resultClassType)){
			return __ajaxNodes(query, reader, readEvent);
		}
		
		if(AbstractNode.class.isAssignableFrom(resultClassType)){
			return __nodes(query, reader, readEvent, __nodeSelect(reader, __loadChildren));
		}

		return __nodes(query, reader, readEvent);
	}
}
